﻿using System;
using System.Text.Json.Nodes;
using System.Threading;
using System.Threading.Tasks;

namespace DotNetCommon.Validate;

/// <summary>
/// json数据校验
/// </summary>
public static class ValidateJsonExtensions
{
    #region JsonNode
    /// <summary>
    /// 当 JsonNode.TryGetValue&lt;long>() 返回true时
    /// </summary>
    public static IValidateContext<JsonNode> WhenInteger(this IValidateContext<JsonNode> ctx, Action<IValidateContext<long>> action)
    {
        if (ctx.StopValidate) return ctx;
        if (ctx.Model is not JsonValue val || !val.TryGetValue<long>(out var num))
        {
            return ctx;
        }
        action(new ValidateContext<long>(num, ctx));
        return ctx;
    }

    /// <summary>
    /// 当 JsonNode.TryGetValue&lt;double>() 返回true时
    /// </summary>
    public static IValidateContext<JsonNode> WhenNumber(this IValidateContext<JsonNode> ctx, Action<IValidateContext<double>> action)
    {
        if (ctx.StopValidate) return ctx;
        if (ctx.Model is not JsonValue val || !val.TryGetValue<double>(out var num))
        {
            return ctx;
        }
        action(new ValidateContext<double>(num, ctx));
        return ctx;
    }
    /// <summary>
    /// 当 JsonNode.TryGetValue&lt;string>() 返回true时
    /// </summary>
    public static IValidateContext<JsonNode> WhenString(this IValidateContext<JsonNode> ctx, Action<IValidateContext<string>> action)
    {
        if (ctx.StopValidate) return ctx;
        if (ctx.Model is not JsonValue val || !val.TryGetValue<string>(out var str))
        {
            return ctx;
        }
        action(new ValidateContext<string>(str, ctx));
        return ctx;
    }


    /// <summary>
    /// 当 JsonNode.TryGetValue&lt;bool>() 返回true时
    /// </summary>
    public static IValidateContext<JsonNode> WhenBoolean(this IValidateContext<JsonNode> ctx, Action<IValidateContext<bool>> action)
    {
        if (ctx.StopValidate) return ctx;
        if (ctx.Model is not JsonValue val || !val.TryGetValue<bool>(out var b))
        {
            return ctx;
        }
        action(new ValidateContext<bool>(b, ctx));
        return ctx;
    }

    /// <summary>
    /// 当这个 JsonNode 是 JsonArray 时
    /// </summary>
    public static IValidateContext<JsonNode> WhenArray(this IValidateContext<JsonNode> ctx, Action<IValidateContext<JsonArray>> action)
    {
        if (ctx.StopValidate) return ctx;
        if (ctx.Model is not JsonArray array)
        {
            return ctx;
        }
        action(new ValidateContext<JsonArray>(array, ctx));
        return ctx;
    }

    /// <summary>
    /// 当这个 JsonNode 是 JsonObject 时
    /// </summary>
    public static IValidateContext<JsonNode> WhenObject(this IValidateContext<JsonNode> ctx, Action<IValidateContext<JsonObject>> action)
    {
        if (ctx.StopValidate) return ctx;
        if (ctx.Model is not JsonObject obj)
        {
            return ctx;
        }
        action(new ValidateContext<JsonObject>(obj, ctx));
        return ctx;
    }

    /// <summary>
    /// 必须为整形数字, 即: JsonNode.TryGetValue&lt;long>() 返回true
    /// <list type="bullet">
    /// <item>如果是, 返回一个新的 ValidateContext</item>
    /// <item>如果不是, 则收集错误信息并返回已经失败的 ValidateContext</item>
    /// </list>
    /// </summary>
    public static IValidateContext<long> MustBeInteger(this IValidateContext<JsonNode> ctx, string errorMessage = null)
    {
        if (ctx.StopValidate) return new ValidateContext<long>(default, null, ctx, true);
        if (ctx.Model is not JsonValue val || !val.TryGetValue<long>(out var num))
        {
            ctx.AddError(ctx.ModelPath, errorMessage ?? $"'{ctx.ModelPath}' 必须是整形!");
            return new ValidateContext<long>(default, null, ctx, true);
        }
        return new ValidateContext<long>(num, ctx);
    }

    /// <summary>
    /// 必须为浮点型数字, 即: 当 JsonNode.TryGetValue&lt;double>() 返回true
    /// <list type="bullet">
    /// <item>如果是, 返回一个新的 ValidateContext</item>
    /// <item>如果不是, 则收集错误信息并返回已经失败的 ValidateContext</item>
    /// </list>
    /// </summary>
    public static IValidateContext<double> MustBeNumber(this IValidateContext<JsonNode> ctx, string errorMessage = null)
    {
        if (ctx.StopValidate) return new ValidateContext<double>(default, null, ctx, true);
        if (ctx.Model is not JsonValue val || !val.TryGetValue<double>(out var num))
        {
            ctx.AddError(ctx.ModelPath, errorMessage ?? $"'{ctx.ModelPath}' 必须是浮点型!");
            return new ValidateContext<double>(default, null, ctx, true);
        }
        return new ValidateContext<double>(num, ctx);
    }

    /// <summary>
    /// 必须为字符串, 即: JsonNode.TryGetValue&lt;string>() 返回true
    /// <list type="bullet">
    /// <item>如果是, 返回一个新的 ValidateContext</item>
    /// <item>如果不是, 则收集错误信息并返回已经失败的 ValidateContext</item>
    /// </list>
    /// </summary>
    public static IValidateContext<string> MustBeString(this IValidateContext<JsonNode> ctx, string errorMessage = null)
    {
        if (ctx.StopValidate) return new ValidateContext<string>(default, null, ctx, true);
        if (ctx.Model is not JsonValue val || !val.TryGetValue<string>(out var str))
        {
            ctx.AddError(ctx.ModelPath, errorMessage ?? $"'{ctx.ModelPath}' 必须是字符串!");
            return new ValidateContext<string>(default, null, ctx, true);
        }
        return new ValidateContext<string>(str, ctx);
    }
    /// <summary>
    /// 必须为布尔值, 即: JsonNode.TryGetValue&lt;bool>() 返回true
    /// <list type="bullet">
    /// <item>如果是, 返回一个新的 ValidateContext</item>
    /// <item>如果不是, 则收集错误信息并返回已经失败的 ValidateContext</item>
    /// </list>
    /// </summary>
    public static IValidateContext<bool> MustBeBoolean(this IValidateContext<JsonNode> ctx, string errorMessage = null)
    {
        if (ctx.StopValidate) return new ValidateContext<bool>(default, null, ctx, true);
        if (ctx.Model is not JsonValue val || !val.TryGetValue<bool>(out var b))
        {
            ctx.AddError(ctx.ModelPath, errorMessage ?? $"'{ctx.ModelPath}' 必须是 True 或 False !");
            return new ValidateContext<bool>(default, null, ctx, true);
        }
        return new ValidateContext<bool>(b, ctx);
    }
    /// <summary>
    /// 必须为数组
    /// <list type="bullet">
    /// <item>如果是, 返回一个新的 ValidateContext</item>
    /// <item>如果不是, 则收集错误信息并返回已经失败的 ValidateContext</item>
    /// </list>
    /// </summary>
    public static IValidateContext<JsonArray> MustBeArray(this IValidateContext<JsonNode> ctx, string errorMessage = null)
    {
        if (ctx.StopValidate) return new ValidateContext<JsonArray>(default, null, ctx, true);
        if (ctx.Model is not JsonArray array)
        {
            ctx.AddError(ctx.ModelPath, errorMessage ?? $"'{ctx.ModelPath}' 必须是数组!");
            return new ValidateContext<JsonArray>(default, null, ctx, true);
        }
        return new ValidateContext<JsonArray>(array, ctx);
    }
    /// <summary>
    /// 必须为对象
    /// <list type="bullet">
    /// <item>如果是, 返回一个新的 ValidateContext</item>
    /// <item>如果不是, 则收集错误信息并返回已经失败的 ValidateContext</item>
    /// </list>
    /// </summary>
    public static IValidateContext<JsonObject> MustBeObject(this IValidateContext<JsonNode> ctx, string errorMessage = null)
    {
        if (ctx.StopValidate) return new ValidateContext<JsonObject>(default, null, ctx, true);
        if (ctx.Model is not JsonObject obj)
        {
            ctx.AddError(ctx.ModelPath, errorMessage ?? $"'{ctx.ModelPath}' 必须是对象!");
            return new ValidateContext<JsonObject>(default, null, ctx, true);
        }
        return new ValidateContext<JsonObject>(obj, ctx);
    }
    #endregion

    #region JsonArray
    /// <summary>
    /// 不能为空
    /// </summary>
    public static IValidateContext<JsonArray> MustNotNullOrEmpty(this IValidateContext<JsonArray> ctx, string errorMessage = null)
    {
        if (ctx.StopValidate) return ctx;
        if ((ctx.Model?.Count ?? 0) == 0)
        {
            ctx.AddError(ctx.ModelPath, errorMessage ?? $"'{ctx.ModelPath}' 集合不能为空!");
        }
        return ctx;
    }
    /// <summary>
    /// 如果集合不为空, 则遍历集合元素
    /// </summary>
    public static void RuleForEach(this IValidateContext<JsonArray> ctx, Action<IValidateContext<JsonNode>> validateAction)
    {
        if (ctx.StopValidate) return;
        if (ctx.Model == null) return;
        //获取prop的name
        for (var i = 0; i < ctx.Model.Count; i++)
        {
            var propName = $"[{i}]";
            var propModel = ctx.Model[i]; ;
            var newCtx = new ValidateContext<JsonNode>(propModel, propName);
            ctx.Children.Add(newCtx);
            newCtx.Parent = ctx;
            validateAction.Invoke(newCtx);
        }
        return;
    }

    /// <summary>
    /// 如果集合不为空, 则遍历集合元素
    /// </summary>
    public static async Task RuleForEachAsync(this IValidateContext<JsonArray> ctx, Func<IValidateContext<JsonNode>, CancellationToken, Task> validateAction)
    {
        if (ctx.StopValidate) return;
        if (ctx.Model == null) return;
        //获取prop的name
        for (var i = 0; i < ctx.Model.Count; i++)
        {
            var propName = $"[{i}]";
            var propModel = ctx.Model[i]; ;
            var newCtx = new ValidateContext<JsonNode>(propModel, propName);
            ctx.Children.Add(newCtx);
            newCtx.Parent = ctx;
            await validateAction.Invoke(newCtx, newCtx.CancellationToken);
        }
        return;
    }
    #endregion

    #region JsonObject
    /// <summary>
    /// 必须包含属性 key
    /// </summary>
    public static IValidateContext<JsonObject> MustContainsKey(this IValidateContext<JsonObject> ctx, string key, string errorMessage = null)
    {
        if (ctx.StopValidate) return ctx;
        if (ctx.Model?.ContainsKey(key) != true)
        {
            ctx.AddError(ctx.ModelPath, errorMessage ?? $"'{ctx.ModelPath}' 必须包含属性 '{key}'!");
        }
        return ctx;
    }

    /// <summary>
    /// 必须包含属性 key
    /// <list type="bullet">
    /// <item>如果是, 返回一个新的 ValidateContext</item>
    /// <item>如果不是, 则收集错误信息并返回已经失败的 ValidateContext</item>
    /// </list>
    /// </summary>
    public static IValidateContext<JsonNode> MustContainsAndRuleForKey(this IValidateContext<JsonObject> ctx, string key, string errorMessage = null)
    {
        if (ctx.StopValidate) return new ValidateContext<JsonNode>(default, null, ctx, true);
        if (ctx.Model?.ContainsKey(key) != true)
        {
            ctx.AddError(ctx.ModelPath, errorMessage ?? $"'{ctx.ModelPath}' 必须包含属性 '{key}'!");
            return new ValidateContext<JsonNode>(default, null, ctx, true);
        }
        return new ValidateContext<JsonNode>(ctx.Model[key], key, ctx, false);
    }

    /// <summary>
    /// 不能为空
    /// </summary>
    public static IValidateContext<JsonObject> MustNotNullOrEmpty(this IValidateContext<JsonObject> ctx, string errorMessage = null)
    {
        if (ctx.StopValidate) return ctx;
        if ((ctx.Model?.Count ?? 0) == 0)
        {
            ctx.AddError(ctx.ModelPath, errorMessage ?? $"'{ctx.ModelPath}' 属性数量必须大于 0 !");
        }
        return ctx;
    }
    /// <summary>
    /// 如果不为null, 则遍历属性
    /// </summary>
    public static void RuleForEach(this IValidateContext<JsonObject> ctx, Action<IValidateContext<JsonNode>> validateAction)
    {
        if (ctx.StopValidate) return;
        if (ctx.Model == null) return;
        //获取prop的name
        foreach (var kv in ctx.Model)
        {
            var propName = $"{kv.Key}";
            var propModel = kv.Value;
            var newCtx = new ValidateContext<JsonNode>(propModel, propName);
            ctx.Children.Add(newCtx);
            newCtx.Parent = ctx;
            validateAction.Invoke(newCtx);
        }
        return;
    }
    /// <summary>
    /// 如果不为null, 则遍历属性
    /// </summary>
    public static async Task RuleForEachAsync(this IValidateContext<JsonObject> ctx, Func<IValidateContext<JsonNode>, CancellationToken, Task> validateAction)
    {
        if (ctx.StopValidate) return;
        if (ctx.Model == null) return;
        //获取prop的name
        foreach (var kv in ctx.Model)
        {
            var propName = $"{kv.Key}";
            var propModel = kv.Value;
            var newCtx = new ValidateContext<JsonNode>(propModel, propName);
            ctx.Children.Add(newCtx);
            newCtx.Parent = ctx;
            await validateAction.Invoke(newCtx, newCtx.CancellationToken);
        }
        return;
    }
    /// <summary>
    /// 当指定属性存在时就访问这个属性
    /// </summary>
    public static IValidateContext<JsonObject> WhenContainsAndRuleForKey(this IValidateContext<JsonObject> ctx, string key, Action<IValidateContext<JsonNode>> validateAction)
    {
        if (ctx.StopValidate) return ctx;
        if ((ctx.Model?.ContainsKey(key) ?? false) == false) return ctx;

        var newCtx = new ValidateContext<JsonNode>(ctx.Model[key], key, ctx, false);
        validateAction(newCtx);
        return ctx;
    }
    #endregion
}